package com.itheima.filter;

import com.itheima.balance.ZhzGrayLoadBalancer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.function.Consumer;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ALREADY_PREFIXED_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;

/**
 * @program: springcloud
 * @description: 灰度发布使用, 用来拦截用户请求, 根据ip, id, 权重, 版本, 接口(业务)实现灰度发布
 * 仿写 ReactiveLoadBalancerClientFilter
 * @author: zhanghz001
 * @create: 2021-08-02 15:37
 **/
// @Configuration
public class ZhzGrayFilter implements GlobalFilter, Ordered {
    private static final Log log = LogFactory.getLog(ZhzGrayFilter.class);
    
    //负载均衡构建工厂对象
    private LoadBalancerClientFactory clientFactory;
    
    //负载均衡器属性对象
    private LoadBalancerProperties properties;
    
    public ZhzGrayFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
        this.clientFactory = clientFactory;
        this.properties = properties;
    }
    
    /**
     * 拦截器所有对象,进行过滤
     *
     * @param exchange 用户所有的请求响应对象
     * @param chain    链路调用对象
     * @return 进行下一个调用链
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取scheme，bootstrap.yml中 uri: scheme://xxx
        URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
        
        String schemePrefix = exchange.getAttribute(GATEWAY_ALREADY_PREFIXED_ATTR);
        
        // 判断当前的scheme是否为灰度服务,定义scheme的服务微grayLb
        if (uri != null && ("grayLb".equals(uri.getScheme()) || "grayLb".equals(schemePrefix))) {
            //根据不同策略获取服务实例
            Mono<Response<ServiceInstance>> responseInstance = this.choose(exchange);
            
            //封装灰度实例地址信息（将 http://car-version1/car 换成http://192.168.211.1:18082/car）
            responseInstance = setInstanceInfo(responseInstance, exchange);
            
            //灰度服务调用链路
            return responseInstance.then(chain.filter(exchange));
            
        }
        //
        return chain.filter(exchange);
    }
    
    /**
     * 将地址中的域名,替换成正确的服务ip和端口号
     *
     * @param serviceInstanceResponse
     * @param exchange
     * @return
     */
    private Mono<Response<ServiceInstance>> setInstanceInfo(
            Mono<Response<ServiceInstance>> serviceInstanceResponse,
            ServerWebExchange exchange) {
        return serviceInstanceResponse.doOnNext(
                new Consumer<Response<ServiceInstance>>() {
                    @Override
                    public void accept(Response<ServiceInstance> serviceInstance) {
                        //获取url
                        URI uri = exchange.getRequest().getURI();
                        
                        //获取真实服务器的地址信息ip,端口
                        DelegatingServiceInstance delegatingServiceInstance =
                                new DelegatingServiceInstance(serviceInstance.getServer(), null);
                        
                        //将用户请求的url地址换取真实的ip,端口
                        URI requestURI = LoadBalancerUriTools.reconstructURI(delegatingServiceInstance, uri);
                        
                        //将requestURI添加到exchange属性中
                        exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestURI);
                    }
                }
        );
    }
    
    private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
        //获取uri
        URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
        ZhzGrayLoadBalancer zhzGrayLoadBalancer = new ZhzGrayLoadBalancer(
                //服务集合封装
                clientFactory.getLazyProvider(uri.getHost(), ServiceInstanceListSupplier.class),
                //服务名字
                uri.getHost()
        );
        
        //调用GrayLoadBalancer.choose() IP ID header
        HttpHeaders headers = exchange.getRequest().getHeaders();
        return zhzGrayLoadBalancer.choose(new DefaultRequest<>(headers));
    }
    
    //最后执行,也就是最后进行拦截
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}
